#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/list.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/sysfs.h>

#define MYCDEV_NAME "mycdev_simple"
#define MYCDEV_CLASS "mycdev_class"
#define CDEV_NUM 2
#define BUFFER_SIZE 256
#define MAX_STRING_LENGTH 256

#define IOCTL_MODE_SWITCH _IO('M',0)

static char my_buffer[BUFFER_SIZE];

static char my_property[MAX_STRING_LENGTH];

// modo模式，0代表正常写读，1代表正常写，但是读出是逆序
int mode = 0;

static struct class *mycdev_class;
static struct cdev cdevs[CDEV_NUM];
static struct device devices[CDEV_NUM];
// static struct device_attribute attributes[CDEV_NUM];
static dev_t dev_num;
 
 
// static int strLen(const char *s);
static int mycdev_open(struct inode *inode, struct file *file);
static ssize_t mycdev_read(struct file *file, char __user *buff, size_t len , loff_t *pos);
static ssize_t mycdev_write(struct file *, const char __user *buff, size_t len, loff_t *pos);
static int mycdev_release(struct inode *inode, struct file *file);
static long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
static ssize_t mycdev_property_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count);
static ssize_t mycdev_property_show(struct device *dev, struct device_attribute *attr, char *buf);
static struct file_operations op_f={
	.read=mycdev_read,
	.write=mycdev_write,
	.open=mycdev_open,
	.release=mycdev_release,
    .unlocked_ioctl=mycdev_ioctl,
};
 


 
 

static int mycdev_open(struct inode *inode, struct file *file)
{

	printk("call mycdev_open\n");
	return 0;
}


// 从设备文件读
static ssize_t mycdev_read(struct file *file, char __user *buffer, size_t length, loff_t *offset)
{
    int i, j;
    ssize_t count = 0;    

    if (mode == 0) {
        for (j = 0; j < strlen(my_buffer) && count < length; j++) {
            if (put_user(my_buffer[j], &buffer[count]) != 0)
                return -EFAULT;
            count++;
        }
    } else if (mode == 1) {
        for (i = strlen(my_buffer) - 1; i >= 0 && count < length; i--) {
            if (put_user(my_buffer[i], &buffer[count]) != 0)
                return -EFAULT;
            count++;
        }
    }

    return count;
}
 

// 往设备文件写
static ssize_t mycdev_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset)
{
    printk("call mycdev_write\n");
    memset(my_buffer, 0, sizeof(my_buffer));
    if (copy_from_user(my_buffer, buffer, length) != 0)
        return -EFAULT;
    return length;
}

// 写入设备属性
static ssize_t mycdev_property_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) {
    size_t len = min((int)count, MAX_STRING_LENGTH - 1);
    strncpy(my_property, buf, len);
    my_property[len] = '\0';
    return count;
}

// 读取设备属性
static ssize_t mycdev_property_show(struct device *dev, struct device_attribute *attr, char *buf) {
    if(mode==0) {
        size_t len = strlen(my_property);
        char reversed_str[MAX_STRING_LENGTH];
        int i, j;
        // 倒序复制字符串到反转字符串数组
        for (i = len - 1, j = 0; i >= 0; i--, j++) {
            reversed_str[j] = my_property[i];
        }
        reversed_str[len] = '\0';
        // 将反转的字符串复制到读缓冲区
        strncpy(buf, reversed_str, len + 1);
        return len;
    }else if(mode==1) {
        return sprintf(buf, "%s\n", my_property);
    }

    return sprintf(buf, "%s\n", my_property);
}
 // 设备属性结构体
static DEVICE_ATTR(my_property, 0664, mycdev_property_show, mycdev_property_store);

static int mycdev_release(struct inode *inode, struct file *file)
{
	printk("call mycdev_release\n");
	return 0;
}
 

// static int strLen(const char *s)
// {
// 	int len;
// 	len=0;
// 	while((*s)!=0)
// 	{
// 		s++;
// 		len++;
// 	}
// 	return len;
// }
 
static long mycdev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    switch(cmd) {
        case IOCTL_MODE_SWITCH:
            if (mode == 0) {
                mode = 1;
            } else if (mode == 1) {
                mode = 0;
            }
            break;
        default:
            return -ENOTTY;// 未知命令
    }
    return 0;
}

static int __init simple_chardev_init(void)
{
	int i,retVal;

    // 32 bit variables
    dev_t curr_devnum; 

    /**
        2023.6.6 fjh:
        allo_chrdev_region:
        Assigning a device number without a device number
        parameter1: &dev_num, Return the device number of the successful application;
        parameter2: 0, the start of the minor device number, usually 0;
        parameter3: CODE_NUM, the number of devices;
        parameter4: MYCDEV_CLASS, dev's string name, will show in /proc/devices

        if we already have the device number, we should call 'register_chrdev_region':
        code:
            dev = MKDEV(major,minor);
            ret = register_chrdev_region(dev,devNum,devName);

    */
    retVal=alloc_chrdev_region(&dev_num,0,CDEV_NUM,MYCDEV_CLASS);
    if(retVal!=0)
    {
    	printk("alloc_chrdev_region error %d\n",retVal);
    	return retVal;
    }

    /**
        2023.6.6 fjh:
        To create a class, a folder will be created under /sys/class
    */
    mycdev_class=class_create(THIS_MODULE,MYCDEV_CLASS);


    /**
        2023.6.6 fjh:

    */
    for(i=0;i<CDEV_NUM;i++)
    {
    	cdev_init(&cdevs[i],&op_f);
    	cdevs[i].owner=THIS_MODULE;
    	curr_devnum=MKDEV(MAJOR(dev_num),MINOR(dev_num)+i);
    	retVal=cdev_add(&cdevs[i],curr_devnum,1);
        printk("cdev_add retVal %d\n",retVal);
        // Safe code should theoretically be added here, because these create statements can fail
    	// 创建设备文件
        devices[i]=*(device_create(mycdev_class,NULL,curr_devnum,NULL,MYCDEV_NAME "%d",i));
        // 创建设备属性文件，题目里没有这句，
        // 缺这句的话，不会创建/sys/devices/virtual/mycdev_class/mycdev_simple0/my_property的
        device_create_file(&devices[i],&dev_attr_my_property);
        printk("device_create %p\n",&devices[i]);
        
    }
	return retVal;
}


 
static void __exit simple_chardev_exit(void)
{
    dev_t _devnum;
    int i;
 
    for(i=0;i<CDEV_NUM;i++)
    {
        _devnum=MKDEV(MAJOR(dev_num),MINOR(dev_num)+i);

        device_destroy(mycdev_class, _devnum);
        cdev_del(&(cdevs[i]));
    }
    class_destroy(mycdev_class);
 
    unregister_chrdev_region(dev_num,CDEV_NUM);
    printk("simple_chardev_exit\n");
    return;
}
module_init(simple_chardev_init);
module_exit(simple_chardev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxxxxx");
MODULE_DESCRIPTION("123");